package com.longtxt.core.ble

import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.Manifest
import androidx.core.content.ContextCompat
import timber.log.Timber
import android.media.MediaPlayer
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import com.longtxt.core.ble.model.BaseBleResponse
import com.longtxt.core.ble.impl.BleRepository
import com.longtxt.core.ble.impl.BleRepositoryImpl
import com.longtxt.core.ble.model.BoundDevice
import com.longtxt.core.ble.model.Daily
import com.longtxt.core.ble.model.DeviceItem
import com.longtxt.core.ble.model.Health
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import java.io.File

object BleManager : BleRepository {
    private val repo = BleRepositoryImpl()
    //For jie li watch

    private var mediaPlayer: MediaPlayer? = null
    private var vibrator: Vibrator? = null

    private var isPlayingMusic = false

    override var connectState: MutableStateFlow<BlueEventState>
        get() = repo.connectState
        set(value) {
            repo.connectState = value
        }
    override var bandConfig: BoundDevice
        get() = repo.bandConfig
        set(value) {}
    override var isJLieWatch: Boolean
        get() = repo.isJLieWatch
        set(value) {}
    override var baseFileUrl: File
        get() = repo.baseFileUrl
        set(value) {}
    override var notificationFlag: Int
        get() = repo.notificationFlag
        set(value) {
            repo.notificationFlag = value
        }
    override var isManualConnect: Boolean
        get() = repo.isManualConnect
        set(value) {}
    override var isRoundWatch: Boolean
        get() = repo.isRoundWatch
        set(value) {}
    override var isSpecialWatch: Boolean
        get() = repo.isSpecialWatch
        set(value) {}

    override var notification: Notification
        get() = repo.notification
        set(value) {}

    override fun disconnect() {
        repo.disconnect()
    }

    override fun updateBondDevice(name: String, mac: String) = repo.updateBondDevice(name, mac)
    override fun switchFixedWatch(
        type: Int,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) {
        repo.switchFixedWatch(type, response)
    }

    override fun stopBleScan() {

    }

    override fun resetBleState() {
        repo.resetBleState()
    }

    override fun setWeatherRequestListener(listener: () -> Unit) =
        repo.setWeatherRequestListener(listener)

    override fun setHealthDataListener(listener: (BaseBleResponse<Health>) -> Unit) =
        repo.setHealthDataListener(listener)
    override fun setCameraShakeListener(listener: (Boolean) -> Unit) =
        repo.setCameraShakeListener(listener)

    override fun setCallControlListener(listener: (Boolean) -> Unit) {
        repo.setCallControlListener(listener)
    }

    override fun addBleLogListener(listener: (String) -> Unit) = repo.addBleLogListener(listener)

    override fun removeCameraShakeListener() = repo.removeCameraShakeListener()

    override fun setFindPhoneListener(listener: (Boolean) -> Unit) =
        repo.setFindPhoneListener(listener)

    override fun removeFindPhoneListener() = repo.removeFindPhoneListener()
    override fun setCameraOpenListener(listener: (Boolean) -> Unit) =
        repo.setCameraOpenListener(listener)

    override fun removeCameraOpenListener() = repo.removeCameraOpenListener()
    override fun setDailyDataListener(listener: (BaseBleResponse<Daily>) -> Unit) =
        repo.setDailyDataListener(listener)

    override fun removeDailyDataListener() = repo.removeDailyDataListener()
    override fun initializeSdk(
        context: Context, channelId: String,
        channelName: String,
        contentTitle: String,
        contentText: String,
        smallIcon: Int,
        pendingIntent: PendingIntent
    ) {
        vibrator = context.getSystemService(Vibrator::class.java) as Vibrator
        repo.initializeSdk(
            context,
            channelId,
            channelName,
            contentTitle,
            contentText,
            smallIcon,
            pendingIntent
        )
    }

    @JvmStatic
    fun tryStartForegroundIfNeeded(context: Context) {
        // Android 12+ requires BLUETOOTH_* runtime permissions
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val hasConnect = ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.BLUETOOTH_CONNECT
            ) == PackageManager.PERMISSION_GRANTED
            val hasScan = ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.BLUETOOTH_SCAN
            ) == PackageManager.PERMISSION_GRANTED
            if (!hasConnect || !hasScan) {
                Timber.w("tryStartForegroundIfNeeded: Missing Bluetooth perms: connect=$hasConnect scan=$hasScan")
                return
            }
        }

        // Android 13+ requires notification permission to post foreground notification
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val hasNotif = ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.POST_NOTIFICATIONS
            ) == PackageManager.PERMISSION_GRANTED
            if (!hasNotif) {
                Timber.w("tryStartForegroundIfNeeded: Missing POST_NOTIFICATIONS permission")
                return
            }
        }

        Timber.i("tryStartForegroundIfNeeded: Starting BleService in foreground")
        repo.starBleService(context, true)
    }

    @JvmStatic
    fun getMissingForegroundPrerequisites(context: Context): List<String> {
        val missing = mutableListOf<String>()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                missing.add(Manifest.permission.BLUETOOTH_CONNECT)
            }
            if (ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_SCAN
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                missing.add(Manifest.permission.BLUETOOTH_SCAN)
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.POST_NOTIFICATIONS
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                missing.add(Manifest.permission.POST_NOTIFICATIONS)
            }
        }
        return missing
    }


    override fun starBleService(context: Context, needForeService: Boolean) =
        repo.starBleService(context, needForeService)

    override fun connectBleDevice(
        device: DeviceItem,
        needBond: Boolean,
        timeoutMs: Long,
        resp: (BaseBleResponse<Boolean>) -> Unit
    ) =
        repo.connectBleDevice(device, needBond, timeoutMs, resp)


    override fun startConnectBle() = repo.startConnectBle()

    override fun bleStateToConnected() = repo.bleStateToConnected()

    override fun bleStateToDisconnected() = repo.bleStateToDisconnected()
    override fun getBandConfig() = repo.getBandConfig()

    override fun getUserInfo() = repo.getUserInfo()

    override fun getSupportedFunc() = repo.getSupportedFunc()
    override fun changeNotifySetting(value: Int) = repo.changeNotifySetting(value)

    override fun getExamineData() =
        repo.getExamineData()

    override fun getSportData(day: Int) = repo.getSportData(day)

    override fun getSoftVersion() = repo.getSoftVersion()

    override fun postOSPlatform(
        data: Byte,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postOSPlatform(data, response)

    override fun postLanguage(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postLanguage(data, response)

    override fun postFindBand(response: (BaseBleResponse<Boolean>) -> Unit) =
        repo.postFindBand(response)

    override fun postBandReset(response: (BaseBleResponse<Boolean>) -> Unit) =
        repo.postBandReset(response)

    override fun postAlarm(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postAlarm(data, response)

    override fun postAntiLost(
        on: Boolean,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postAntiLost(on, response)

    override fun postHeartRate(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postHeartRate(data, response)

    override fun postLightUp(
        on: Boolean,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postLightUp(on, response)

    override fun postScreenSaverInterval(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postScreenSaverInterval(data, response)

    override fun postTimeFormat(data: Byte) = repo.postTimeFormat(data)

    override fun postUserInfo(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postUserInfo(data, response)

    override fun postWeather(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postWeather(data, response)

    override fun postCameraState(
        isOn: Boolean,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postCameraState(isOn, response)

    override fun postScreenStart(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) =
        repo.postScreenStart(data, response)

    override fun postScreenData(data: ByteArray, response: (BaseBleResponse<Boolean>) -> Unit) =
        repo.postScreenData(data, response)

    override fun postMarketScreenStart(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postMarketScreenStart(data, response)

    override fun postMarketScreenData(
        data: ByteArray,
        response: (BaseBleResponse<Boolean>) -> Unit
    ) = repo.postMarketScreenData(data, response)

    override fun postScreenParams(data: ByteArray) = repo.postScreenParams(data)

    override fun writeCommandData(data: ByteArray) = repo.writeCommandData(data)

    override fun getHeadSetMacAddress(response: (BaseBleResponse<ByteArray>) -> Unit) =
        repo.getHeadSetMacAddress(response)

    override fun postInitialConnection(isInitial: Boolean) = repo.postInitialConnection(isInitial)


    @SuppressLint("MissingPermission")
    fun playMp3AndVibrate(context: Context, musicSource: Int) {
        if (isPlayingMusic) {
            return
        }
        MediaPlayer.create(context, musicSource)?.let {
            mediaPlayer = it
            mediaPlayer?.isLooping = true
            mediaPlayer?.start()
        }
        vibrator?.let {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val effect = VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)
                it.vibrate(effect)
            } else {
                it.vibrate(500)
            }
        }

        MainScope().launch {
            delay(20000)
            stopMusic()
        }

    }

    @SuppressLint("MissingPermission")
    fun stopMusic() {
        mediaPlayer?.stop()
        mediaPlayer?.release()
        mediaPlayer = null
        vibrator?.hasVibrator().also {
            if (it == true) vibrator?.cancel()
        }
    }

}

sealed class BlueEventState {
    data object Initial : BlueEventState()
    data object BluetoothConnecting : BlueEventState()
    data object BluetoothConnected : BlueEventState()
    data object BluetoothDisConnected : BlueEventState()
    data class BluetoothReady(val device: BoundDevice) : BlueEventState()

}
